<?php

/**
 * JPGraph v4.0.3
 */

namespace lemon\JpGraph\Plot;

/*
 * File:		JPGRAPH_GRADIENT.PHP
 * // Description: Create a color gradient
 * // Created:	 2003-02-01
 * // Ver:		 $Id: jpgraph_gradient.php 1761 2009-08-01 08:31:28Z ljp $
 * //
 * // Copyright (c) Asial Corporation. All rights reserved.
 */
use lemon\JpGraph\Util;

/**
 * @class Gradient
 * // Description: Handles gradient fills. This is to be
 * // considered a "friend" class of Class Image.
 */
class Gradient
{
	private $img;
	private $numcolors = 100;

	/**
	 * CONSTRUCTOR.
	 *
	 * @param mixed $img
	 */
	public function __construct(&$img)
	{
		$this->img = $img;
	}

	public function SetNumColors($aNum)
	{
		$this->numcolors = $aNum;
	}

	/**
	 * PUBLIC METHODS.
	 *
	 * @param mixed $xl
	 * @param mixed $yt
	 * @param mixed $xr
	 * @param mixed $yb
	 * @param mixed $from_color
	 * @param mixed $to_color
	 * @param mixed $style
	 */
	// Produce a gradient filled rectangle with a smooth transition between
	// two colors.
	// ($xl,$yt)  Top left corner
	// ($xr,$yb) Bottom right
	// $from_color Starting color in gradient
	// $to_color End color in the gradient
	// $style  Which way is the gradient oriented?
	public function FilledRectangle($xl, $yt, $xr, $yb, $from_color, $to_color, $style = 1)
	{
		$this->img->SetLineWeight(1);
		switch ($style) {
			case GRAD_VER:
				$steps = ceil(abs($xr - $xl) + 1);
				$delta = $xr >= $xl ? 1 : -1;
				$this->GetColArray($from_color, $to_color, $steps, $colors, $this->numcolors);
				for ($i = 0, $x = $xl; $i < $steps; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yt, $x, $yb);
					$x += $delta;
				}

				break;
			case GRAD_HOR:
				$steps = ceil(abs($yb - $yt) + 1);
				$delta = $yb >= $yt ? 1 : -1;
				$this->GetColArray($from_color, $to_color, $steps, $colors, $this->numcolors);
				for ($i = 0, $y = $yt; $i < $steps; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($xl, $y, $xr, $y);
					$y += $delta;
				}

				break;
			case GRAD_MIDHOR:
				$steps = ceil(abs($yb - $yt) / 2);
				$delta = $yb >= $yt ? 1 : -1;
				$this->GetColArray($from_color, $to_color, $steps, $colors, $this->numcolors);
				for ($y = $yt, $i = 0; $i < $steps; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($xl, $y, $xr, $y);
					$y += $delta;
				}
				--$i;
				if (abs($yb - $yt) % 2 == 1) {
					--$steps;
				}
				for ($j = 0; $j < $steps; ++$j, --$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($xl, $y, $xr, $y);
					$y += $delta;
				}
				$this->img->Line($xl, $y, $xr, $y);

				break;
			case GRAD_MIDVER:
				$steps = ceil(abs($xr - $xl) / 2);
				$delta = $xr >= $xl ? 1 : -1;
				$this->GetColArray($from_color, $to_color, $steps, $colors, $this->numcolors);
				for ($x = $xl, $i = 0; $i < $steps; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}
				--$i;
				if (abs($xr - $xl) % 2 == 1) {
					--$steps;
				}
				for ($j = 0; $j < $steps; ++$j, --$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}
				$this->img->Line($x, $yb, $x, $yt);

				break;
			case GRAD_WIDE_MIDVER:
				$diff	  = ceil(abs($xr - $xl));
				$steps	 = floor(abs($diff) / 3);
				$firststep = $diff - 2 * $steps;
				$delta	 = $xr >= $xl ? 1 : -1;
				$this->GetColArray($from_color, $to_color, $firststep, $colors, $this->numcolors);
				for ($x = $xl, $i = 0; $i < $firststep; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}
				--$i;
				$this->img->current_color = $colors[$i];
				for ($j = 0; $j < $steps; ++$j) {
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}

				for ($j = 0; $j < $steps; ++$j, --$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}

				break;
			case GRAD_WIDE_MIDHOR:
				$diff	  = ceil(abs($yb - $yt));
				$steps	 = floor(abs($diff) / 3);
				$firststep = $diff - 2 * $steps;
				$delta	 = $yb >= $yt ? 1 : -1;
				$this->GetColArray($from_color, $to_color, $firststep, $colors, $this->numcolors);
				for ($y = $yt, $i = 0; $i < $firststep; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($xl, $y, $xr, $y);
					$y += $delta;
				}
				--$i;
				$this->img->current_color = $colors[$i];
				for ($j = 0; $j < $steps; ++$j) {
					$this->img->Line($xl, $y, $xr, $y);
					$y += $delta;
				}
				for ($j = 0; $j < $steps; ++$j, --$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($xl, $y, $xr, $y);
					$y += $delta;
				}

				break;
			case GRAD_LEFT_REFLECTION:
				$steps1 = ceil(0.3 * abs($xr - $xl));
				$delta  = $xr >= $xl ? 1 : -1;

				$from_color  = $this->img->rgb->Color($from_color);
				$adj		 = 1.4;
				$m		   = ($adj - 1.0) * (255 - min(255, min($from_color[0], min($from_color[1], $from_color[2]))));
				$from_color2 = [min(255, $from_color[0] + $m),
					min(255, $from_color[1] + $m), min(255, $from_color[2] + $m), ];

				$this->GetColArray($from_color2, $to_color, $steps1, $colors, $this->numcolors);
				$n = safe_count($colors);
				for ($x = $xl, $i = 0; $i < $steps1 && $i < $n; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}
				$steps2 = max(1, ceil(0.08 * abs($xr - $xl)));
				$this->img->SetColor($to_color);
				for ($j = 0; $j < $steps2; ++$j) {
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}
				$steps = abs($xr - $xl) - $steps1 - $steps2;
				$this->GetColArray($to_color, $from_color, $steps, $colors, $this->numcolors);
				$n = safe_count($colors);
				for ($i = 0; $i < $steps && $i < $n; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}

				break;
			case GRAD_RIGHT_REFLECTION:
				$steps1 = ceil(0.7 * abs($xr - $xl));
				$delta  = $xr >= $xl ? 1 : -1;

				$this->GetColArray($from_color, $to_color, $steps1, $colors, $this->numcolors);
				$n = safe_count($colors);
				for ($x = $xl, $i = 0; $i < $steps1 && $i < $n; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}
				$steps2 = max(1, ceil(0.08 * abs($xr - $xl)));
				$this->img->SetColor($to_color);
				for ($j = 0; $j < $steps2; ++$j) {
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}

				$from_color = $this->img->rgb->Color($from_color);
				$adj		= 1.4;
				$m		  = ($adj - 1.0) * (255 - min(255, min($from_color[0], min($from_color[1], $from_color[2]))));
				$from_color = [min(255, $from_color[0] + $m),
					min(255, $from_color[1] + $m), min(255, $from_color[2] + $m), ];

				$steps = abs($xr - $xl) - $steps1 - $steps2;
				$this->GetColArray($to_color, $from_color, $steps, $colors, $this->numcolors);
				$n = safe_count($colors);
				for ($i = 0; $i < $steps && $i < $n; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}

				break;
			case GRAD_CENTER:
				$steps = ceil(min(($yb - $yt) + 1, ($xr - $xl) + 1) / 2);
				$this->GetColArray($from_color, $to_color, $steps, $colors, $this->numcolors);
				$dx = ($xr - $xl) / 2;
				$dy = ($yb - $yt) / 2;
				$x  = $xl;
				$y  = $yt;
				$x2 = $xr;
				$y2 = $yb;
				$n  = safe_count($colors);
				for ($x = $xl, $i = 0; $x < $xl + $dx && $y < $yt + $dy && $i < $n; ++$x, ++$y, --$x2, --$y2, ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Rectangle($x, $y, $x2, $y2);
				}
				$this->img->Line($x, $y, $x2, $y2);

				break;
			case GRAD_RAISED_PANEL:
				// right to left
				$steps1 = $xr - $xl;
				$delta  = $xr >= $xl ? 1 : -1;
				$this->GetColArray($to_color, $from_color, $steps1, $colors, $this->numcolors);
				$n = safe_count($colors);
				for ($x = $xl, $i = 0; $i < $steps1 && $i < $n; ++$i) {
					$this->img->current_color = $colors[$i];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}

				// left to right
				$xr -= 3;
				$xl += 3;
				$yb -= 3;
				$yt += 3;
				$steps2 = $xr - $xl;
				$delta  = $xr >= $xl ? 1 : -1;
				for ($x = $xl, $j = $steps2; $j >= 0; --$j) {
					$this->img->current_color = $colors[$j];
					$this->img->Line($x, $yb, $x, $yt);
					$x += $delta;
				}

				break;
			case GRAD_DIAGONAL:
				// use the longer dimension to determine the required number of steps.
				// first loop draws from one corner to the mid-diagonal and the second
				// loop draws from the mid-diagonal to the opposing corner.
				if ($xr - $xl > $yb - $yt) {
					// width is greater than height -> use x-dimension for steps
					$steps = $xr - $xl;
					$delta = $xr >= $xl ? 1 : -1;
					$this->GetColArray($from_color, $to_color, $steps * 2, $colors, $this->numcolors);
					$n = safe_count($colors);

					for ($x = $xl, $i = 0; $i < $steps && $i < $n; ++$i) {
						$this->img->current_color = $colors[$i];
						$y						= $yt + ($i / $steps) * ($yb - $yt) * $delta;
						$this->img->Line($x, $yt, $xl, $y);
						$x += $delta;
					}

					for ($x = $xl, $i = 0; $i < $steps && $i < $n; ++$i) {
						$this->img->current_color = $colors[$steps + $i];
						$y						= $yt + ($i / $steps) * ($yb - $yt) * $delta;
						$this->img->Line($x, $yb, $xr, $y);
						$x += $delta;
					}
				} else {
					// height is greater than width -> use y-dimension for steps
					$steps = $yb - $yt;
					$delta = $yb >= $yt ? 1 : -1;
					$this->GetColArray($from_color, $to_color, $steps * 2, $colors, $this->numcolors);
					$n = safe_count($colors);

					for ($y = $yt, $i = 0; $i < $steps && $i < $n; ++$i) {
						$this->img->current_color = $colors[$i];
						$x						= $xl + ($i / $steps) * ($xr - $xl) * $delta;
						$this->img->Line($x, $yt, $xl, $y);
						$y += $delta;
					}

					for ($y = $yt, $i = 0; $i < $steps && $i < $n; ++$i) {
						$this->img->current_color = $colors[$steps + $i];
						$x						= $xl + ($i / $steps) * ($xr - $xl) * $delta;
						$this->img->Line($x, $yb, $xr, $y);
						$x += $delta;
					}
				}

				break;
			default:
				Util\JpGraphError::RaiseL(7001, $style);
				//("Unknown gradient style (=$style).");
				break;
		}
	}

	// Fill a special case of a polygon with a flat bottom
	// with a gradient. Can be used for filled line plots.
	// Please note that this is NOT a generic gradient polygon fill
	// routine. It assumes that the bottom is flat (like a drawing
	// of a mountain)
	public function FilledFlatPolygon($pts, $from_color, $to_color)
	{
		if (safe_count($pts) == 0) {
			return;
		}

		$maxy = $pts[1];
		$miny = $pts[1];
		$n	= safe_count($pts);
		for ($i = 0, $idx = 0; $i < $n; $i += 2) {
			$x	= round($pts[$i]);
			$y	= round($pts[$i + 1]);
			$miny = min($miny, $y);
			$maxy = max($maxy, $y);
		}

		$colors = [];
		$this->GetColArray($from_color, $to_color, abs($maxy - $miny) + 1, $colors, $this->numcolors);
		for ($i = $miny, $idx = 0; $i <= $maxy; ++$i) {
			$colmap[$i] = $colors[$idx++];
		}

		$n   = safe_count($pts) / 2;
		$idx = 0;
		while ($idx < $n - 1) {
			$p1 = [round($pts[$idx * 2]), round($pts[$idx * 2 + 1])];
			$p2 = [round($pts[++$idx * 2]), round($pts[$idx * 2 + 1])];

			// Find the largest rectangle we can fill
			$y = max($p1[1], $p2[1]);
			for ($yy = $maxy; $yy > $y; --$yy) {
				$this->img->current_color = $colmap[$yy];
				$this->img->Line($p1[0], $yy, $p2[0] - 1, $yy);
			}

			if ($p1[1] == $p2[1]) {
				continue;
			}

			// Fill the rest using lines (slow...)
			$slope = ($p2[0] - $p1[0]) / ($p1[1] - $p2[1]);
			$x1	= $p1[0];
			$x2	= $p2[0] - 1;
			$start = $y;
			if ($p1[1] > $p2[1]) {
				while ($y >= $p2[1]) {
					$x1					   = $slope * ($start - $y) + $p1[0];
					$this->img->current_color = $colmap[$y];
					$this->img->Line($x1, $y, $x2, $y);
					--$y;
				}
			} else {
				while ($y >= $p1[1]) {
					$x2					   = $p2[0] + $slope * ($start - $y);
					$this->img->current_color = $colmap[$y];
					$this->img->Line($x1, $y, $x2, $y);
					--$y;
				}
			}
		}
	}

	/**
	 * PRIVATE METHODS.
	 *
	 * @param mixed $from_color
	 * @param mixed $to_color
	 * @param mixed $arr_size
	 * @param mixed $numcols
	 * @param mixed $colors
	 */
	// Add to the image color map the necessary colors to do the transition
	// between the two colors using $numcolors intermediate colors
	public function GetColArray($from_color, $to_color, $arr_size, &$colors, $numcols = 100)
	{
		if ($arr_size == 0) {
			return;
		}

		// If color is given as text get it's corresponding r,g,b values
		$from_color = $this->img->rgb->Color($from_color);
		$to_color   = $this->img->rgb->Color($to_color);

		$rdelta		= ($to_color[0] - $from_color[0]) / $numcols;
		$gdelta		= ($to_color[1] - $from_color[1]) / $numcols;
		$bdelta		= ($to_color[2] - $from_color[2]) / $numcols;
		$colorsperstep = $numcols / $arr_size;
		$prevcolnum	= -1;
		$from_alpha	= $from_color[3];
		$to_alpha	  = $to_color[3];
		$adelta		= ($to_alpha - $from_alpha) / $numcols;

		for ($i = 0; $i < $arr_size; ++$i) {
			$colnum = floor($colorsperstep * $i);
			if ($colnum == $prevcolnum) {
				$colors[$i] = $colidx;
			} else {
				$r	 = floor($from_color[0] + $colnum * $rdelta);
				$g	 = floor($from_color[1] + $colnum * $gdelta);
				$b	 = floor($from_color[2] + $colnum * $bdelta);
				$alpha = $from_alpha + $colnum * $adelta;

				$colidx	 = $this->img->rgb->Allocate(sprintf('#%02x%02x%02x', $r, $g, $b), $alpha);
				$colors[$i] = $colidx;
			}
			$prevcolnum = $colnum;
		}
	}
} // @class
